package cn.edu.xmu.orderpayment.payment.service;

import cn.edu.xmu.oomall.core.util.ReturnNo;
import cn.edu.xmu.oomall.core.util.ReturnObject;
import cn.edu.xmu.oomall.core.util.Common;
import cn.edu.xmu.orderpayment.payment.constants.Constants;
import cn.edu.xmu.orderpayment.payment.dao.PayDao;
import cn.edu.xmu.orderpayment.payment.microservice.alipayvo.NotifyBody;
import cn.edu.xmu.orderpayment.payment.model.InternalPaymentRetVo;
import cn.edu.xmu.orderpayment.payment.util.baseobject.*;
import cn.edu.xmu.orderpayment.payment.microservice.wechatpayvo.WeChatPayRefundNotifyRetVo;
import cn.edu.xmu.orderpayment.payment.microservice.wechatpayvo.WeChatPayTransactionRetVo;
import cn.edu.xmu.orderpayment.payment.model.bo.ErrorAccount;
import cn.edu.xmu.orderpayment.payment.model.bo.PayPattern;
import cn.edu.xmu.orderpayment.payment.model.bo.Payment;
import cn.edu.xmu.orderpayment.payment.model.bo.Refund;
import cn.edu.xmu.orderpayment.payment.model.po.PaymentPo;
import cn.edu.xmu.orderpayment.payment.model.vo.*;
import cn.edu.xmu.orderpayment.payment.model.po.*;
import cn.edu.xmu.orderpayment.payment.mq.RocketMQService;
import cn.edu.xmu.orderpayment.payment.util.PayMentPatternLoader;
import cn.edu.xmu.orderpayment.payment.util.paypattern.BasePaymentPattern;
import cn.edu.xmu.orderpayment.payment.model.bo.Amount;
import cn.edu.xmu.privilegegateway.annotation.util.JacksonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cn.edu.xmu.orderpayment.payment.util.baseobject.Transaction.TransactionType.PAY;
import static cn.edu.xmu.privilegegateway.annotation.util.Common.*;

/**
 * @author yunlong
 * @date 2021-12-02
 */
@Service
public class PayService {

    private Logger logger = LoggerFactory.getLogger(PayService.class);

    @Autowired
    private PayDao payDao;

    @Autowired
    @Lazy
    private PayMentPatternLoader payMentPatternLoader;

    @Autowired
    private RocketMQService rocketMQService;


    @Transactional(rollbackFor = Exception.class, readOnly = true) //get获取状态，没有业务
    public ReturnObject getPaymentPatternState() {

        List<Map<String, Object>> stateList;
        stateList = new ArrayList<>();
        for (PayPattern.State requestType : PayPattern.State.values()) {
            Map<String, Object> temp = new HashMap<>();
            temp.put("code", requestType.getCode());
            temp.put("name", requestType.getDescription());
            stateList.add(temp);
        }
        return new ReturnObject(stateList);
    }

    /**
     * 用户查询当前有效的支付渠道
     * 传入用户token,校验权限
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public ReturnObject getValidPaymentPattern() {
        ReturnObject res = payDao.getValidPaymentPattern();
        return res;
    }

    /**
     * 商家获取该店铺下支持的所有支付渠道
     * 传入用户token，shopId
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public ReturnObject getPayPatterns() {
        ReturnObject res = payDao.getPayPatterns();
        return res;
    }

    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public ReturnObject getPaymentState() {
        List<Map<String, Object>> stateList;
        stateList = new ArrayList<>();
        for (Payment.State requestType : Payment.State.values()) {
            Map<String, Object> temp = new HashMap<>();
            temp.put("code", requestType.getCode());
            temp.put("name", requestType.getDescription());
            stateList.add(temp);
        }
        return new ReturnObject(stateList);
    }

    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public ReturnObject getRefundState() {
        List<Map<String, Object>> stateList;
        stateList = new ArrayList<>();
        for (Refund.State requestType : Refund.State.values()) {
            Map<String, Object> temp = new HashMap<>();
            temp.put("code", requestType.getCode());
            temp.put("name", requestType.getDescription());
            stateList.add(temp);
        }
        return new ReturnObject(stateList);
    }


    /**
     * 平台管理员查询支付信息
     * 传入token校验权限
     * documentID匹配
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public ReturnObject getPaymentInfo(String documentId, Byte state, ZonedDateTime beginTime, ZonedDateTime endTime, Integer page, Integer pageSize) {
        PaymentPoExample example = new PaymentPoExample();
        PaymentPoExample.Criteria criteria = example.createCriteria();
        if (documentId != null) {
            criteria.andDocumentIdEqualTo(documentId);
        }
        if (state != null) {
            criteria.andStateEqualTo(state);
        }
        if (beginTime != null) {
            criteria.andPayTimeGreaterThanOrEqualTo(beginTime.withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime());
        }
        if (endTime != null) {
            criteria.andPayTimeLessThanOrEqualTo(endTime.withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime());
        }

        ReturnObject res = payDao.getPaymentInfo(example, page, pageSize);
        return res;
    }

    /**
     * 平台管理员查询支付详情
     * 传入token校验权限
     * 支付ID匹配支付单
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public ReturnObject getPaymentDetailInfo(Long id) {
        ReturnObject res = payDao.getPaymentDetailInfo(id);
        if (res.getData() != null) {
            Payment payment = (Payment) res.getData();
            PaymentRetVo paymentRetVo = cloneVo(payment, PaymentRetVo.class);
            paymentRetVo.setAdjust(new SimpleAdminUserVo(payment.getAdjustId(), payment.getAdjustName()));
            paymentRetVo.setCreator(new SimpleAdminUserVo(payment.getCreatorId(), payment.getCreatorName()));
            paymentRetVo.setModifier(new SimpleAdminUserVo(payment.getModifierId(), payment.getModifierName()));
            return new ReturnObject(paymentRetVo);
        }
        return res;
    }

    /**
     * 平台管理员修改支付单
     *
     * @return
     * @author wang xusheng
     */
    @Transactional(rollbackFor = Exception.class)
    public ReturnObject modifyPaymentInfo(Payment paymentBo, Long loginId, String loginName) {
        //支付单不存在
        ReturnObject returnObject = payDao.getPaymentDetailInfo(paymentBo.getId());
        if (returnObject.getCode().equals(ReturnNo.RESOURCE_ID_NOTEXIST)) {
            return returnObject;
        }
        //判断支付单的状态是否为未对账，否则返回507错误
        Payment payment = (Payment) returnObject.getData();
        Payment.State state = payment.getState();
        if (!state.equals(Payment.State.HAS_PAYED)) {
            return new ReturnObject(ReturnNo.STATENOTALLOW, "只有未对账的状态才能修改");
        }
        ReturnObject res = payDao.modifyPaymentInfo(paymentBo, loginId, loginName);
        return res;
    }

    /**
     * 平台管理员查询退款信息
     * 传入用户token,校验权限
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public ReturnObject getRefundInfo(String documentId, Byte state, ZonedDateTime beginTime, ZonedDateTime endTime, Integer page, Integer pageSize) {
        RefundPoExample example = new RefundPoExample();
        RefundPoExample.Criteria criteria = example.createCriteria();
        if (documentId != null) {
            criteria.andDocumentIdEqualTo(documentId);
        }
        if (state != null) {
            criteria.andStateEqualTo(state);
        }
        if (beginTime != null) {
            criteria.andRefundTimeGreaterThanOrEqualTo(beginTime.withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime());
        }
        if (endTime != null) {
            criteria.andRefundTimeLessThanOrEqualTo(endTime.withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime());
        }
        ReturnObject res = payDao.getRefundInfo(example, page, pageSize);
        return res;
    }

    /**
     * 平台管理员查询退款详情
     * 根据id查退款单
     * 需验证该id对应的退款单是否存在
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public ReturnObject getRefundDetailInfo(Long id) {
        ReturnObject res = payDao.getRefundDetailInfo(id);
        return res;
    }

    /**
     * 平台管理员修改退款信息
     * 存在性检验： 需验证该id对应的退款单是否存在
     * 合法性验证： 只有未对账的状态才能修改，用于手工挂账处理，否则出507错误
     *
     * @return
     * @author wang xusheng
     */
    @Transactional(rollbackFor = Exception.class)
    public ReturnObject modifyRefundInfo(Refund refundBo, Long loginId, String loginName) {
        //退款单不存在
        ReturnObject returnObject = payDao.getRefundDetailInfo(refundBo.getId());
        if (returnObject.getCode().equals(ReturnNo.RESOURCE_ID_NOTEXIST)) {
            return returnObject;
        }
        //判断退款单的状态是否为未对账，否则返回507错误
        Refund refund = (Refund) returnObject.getData();
        Refund.State state = refund.getState();
        if (!state.equals(Refund.State.HAS_REFUND)) {
            return new ReturnObject(ReturnNo.STATENOTALLOW, "只有未对账的状态才能修改");
        }
        ReturnObject res = payDao.modifyRefundInfo(refundBo, loginId, loginName);
        return res;
    }

    /**
     * 平台管理员查询所有错帐信息
     * 若参数含id，需验证该id是否存在
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public ReturnObject getErrorAccount(String documentId, Byte state, ZonedDateTime beginTime, ZonedDateTime endTime, Integer page, Integer pageSize) {
        ErrorAccountPoExample example = new ErrorAccountPoExample();
        ErrorAccountPoExample.Criteria criteria = example.createCriteria();
        if (documentId != null) {
            criteria.andDocumentIdEqualTo(documentId);
        }
        if (state != null) {
            criteria.andStateEqualTo(state);
        }
        if (beginTime != null) {
            criteria.andTimeGreaterThanOrEqualTo(beginTime.withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime());
        }
        if (endTime != null) {
            criteria.andTimeLessThanOrEqualTo(endTime.withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime());
        }
        ReturnObject res = payDao.getErrorAccount(example, page, pageSize);
        return res;
    }

    /**
     * 平台管理员查询错账详情
     * 根据id查
     * 需验证该id对应的错账单是否存在
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public ReturnObject getErrorAccountDetailInfo(Long id) {
        ReturnObject res = payDao.getErrorAccountDetailInfo(id);
        return res;
    }

    /**
     * 平台管理员修改错账信息
     * 存在性检验： 需验证该id对应的错账单是否存在
     * 合法性验证： 只有未处理的状态才能修改，用于手工挂账处理，否则出507错误
     *
     * @return
     * @author wang xusheng
     */
    @Transactional(rollbackFor = Exception.class)
    public ReturnObject modifyErrorAccountInfo(ErrorAccount errorAccountBo, Long loginId, String loginName) {
        //错账单不存在
        ReturnObject returnObject = payDao.getErrorAccountDetailInfo(errorAccountBo.getId());
        if (returnObject.getCode().equals(ReturnNo.RESOURCE_ID_NOTEXIST)) {
            return returnObject;
        }
        //判断退款单的状态是否为未对账，否则返回507错误
        ErrorAccount errorAccount = (ErrorAccount) returnObject.getData();
        ErrorAccount.State state = errorAccount.getState();
        if (state.equals(ErrorAccount.State.PROCESSED)) {
            return new ReturnObject(ReturnNo.STATENOTALLOW, "只有未处理的状态才能修改");
        }
        ReturnObject res = payDao.modifyErrorAccountInfo(errorAccountBo, loginId, loginName);
        return res;
    }

    /**
     * 对账
     * 对给定的时间范围内的账单进行对账
     * 返回正确条数、错误条数、第三方流水多出条数
     * 需调用第三方支付平台接口
     *
     * @return
     * @author wang xusheng
     */
    @Transactional(rollbackFor = Exception.class)
    public ReturnObject reconciliation(Long loginId, String loginName, ZonedDateTime beginTime, ZonedDateTime endTime) {
//        LocalDateTime beginTime = beginZonedTime.withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime();
//        LocalDateTime endTime = endZonedTime.withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime();
        int extra = 0, error = 0, success = 0;
        List<Transaction> payTransactions = new ArrayList<>();
        List<Transaction> refundTransactions = new ArrayList<>();
        //由于获取账单只与日期有关因此以LocalDate传参，计算时间段包含的日期数，
        LocalDate beginDate = LocalDate.parse(beginTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        LocalDate endDate = LocalDate.parse(endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        //加1是因为durationDays是时间段包含的日期数，而不是相差的天数
        Long durationDays = endDate.toEpochDay() - beginDate.toEpochDay() + 1;
        LocalDate billDate = beginDate;
        List<PayPatternRetVo> paymentPatterns = (List<PayPatternRetVo>) payDao.getPayPatterns().getData();

        //从beginDate开始，直到endDate，不断获取当日对应支付方式的账单
        while (durationDays > 0) {
            for (PayPatternRetVo payPatternRetVo : paymentPatterns) {
                BasePaymentPattern paymentPattern = getBasePayPatternById(payPatternRetVo.getId());
                BillDownloadInfo billDownloadInfo = paymentPattern.getBillDownloadUrl(billDate);
                List<Transaction> paymentPatternTransactions = paymentPattern.getTransactionsFromCSV(billDownloadInfo.getDownloadUrl());
                for (Transaction transaction : paymentPatternTransactions) {
                    if (transaction.getTransactionType().equals(PAY)) {
                        payTransactions.add(transaction);
                    } else {
                        refundTransactions.add(transaction);
                    }
                }
            }
            billDate.plusDays(1);
            durationDays--;
        }

        try {
            //获取支付单、退款单以及第三方支付平台账单
            List<Payment> payments = (List<Payment>) payDao.getPaymentsByTime(beginTime, endTime).getData();
            for (Payment payment : payments) {
                boolean reconciliation = false;
                for (Transaction transaction : payTransactions) {
                    if (transaction.getRequestSn().equals(String.valueOf(payment.getId()))) {
                        reconciliation = true;
                        //对账成功
                        if (payment.getActualAmount().equals(transaction.getAmount())) {
                            success++;
                            payment.setState(Payment.State.HAS_RECONCILED);
                            payment.setAdjustId(loginId);
                            payment.setAdjustName(loginName);
                            payment.setAdjustTime(LocalDateTime.now());
                            payDao.setPaymentReconciled(payment);
                        }
                        //对账失败
                        else {
                            error++;
                            ErrorAccount errorAccount = new ErrorAccount();
                            errorAccount.setTradeSn(transaction.getTradeSn());
                            errorAccount.setPatternId(payment.getPatternId());
                            errorAccount.setDocumentId(payment.getDocumentId());
                            errorAccount.setIncome(transaction.getAmount());
                            errorAccount.setState(ErrorAccount.State.WAIT_PROCESSED);
                            errorAccount.setTime(transaction.getTransactionTime());
                            errorAccount.setDescr(payment.getDescr());
                            payDao.addErrorAccount(errorAccount, loginId, loginName);
                        }
                        break;
                    }
                }
                //找不到对应的transcation，长账
                if (!reconciliation) {
                    extra++;
                }
            }
            List<Refund> refunds = (List<Refund>) payDao.getRefundsByTime(beginTime, endTime).getData();
            for (Refund refund : refunds) {
                boolean reconciliation = false;
                for (Transaction transaction : refundTransactions) {
                    if (String.valueOf(refund.getId()).equals(transaction.getRequestSn())) {
                        reconciliation = true;
                        //对账成功，修改为已对账
                        if (refund.getAmount().equals(transaction.getAmount())) {
                            success++;
                            refund.setState(Refund.State.HAS_RECONCILED);
                            refund.setAdjustId(loginId);
                            refund.setAdjustName(loginName);
                            refund.setAdjustTime(LocalDateTime.now());
                            payDao.setRefundReconciled(refund);
                        }
                        //对账失败，记录到错账
                        else {
                            error++;
                            ErrorAccount errorAccount = new ErrorAccount();
                            errorAccount.setTradeSn(transaction.getTradeSn());
                            errorAccount.setPatternId(refund.getPatternId());
                            errorAccount.setDocumentId(((Payment) payDao.getPaymentById(refund.getPaymentId()).getData()).getDocumentId());
                            errorAccount.setExpenditure(transaction.getAmount());
                            errorAccount.setState(ErrorAccount.State.WAIT_PROCESSED);
                            errorAccount.setTime(transaction.getTransactionTime());
                            errorAccount.setDescr(refund.getDescr());
                            payDao.addErrorAccount(errorAccount, loginId, loginName);
                        }
                    }
                }
                //找不到对应的transcation，长账
                if (!reconciliation) {
                    extra++;
                }
            }
            return new ReturnObject(new ReconciliationRetVo(success, error, extra));
        } catch (Exception e) {
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR, e.getMessage() + "--11");
        }
    }

    /**
     * 微信支付回调通知
     * 对收到的支付结果，修改相应支付单信息
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public ReturnObject wechatPaymentNotify(WeChatPayTransactionRetVo weChatPayTransactionRetVo) {

        Payment payment = (Payment) payDao.getPaymentById(Long.valueOf(weChatPayTransactionRetVo.getOutTradeNo())).getData();
        if (payment == null) {
            return new ReturnObject(ReturnNo.RESOURCE_ID_NOTEXIST);
        }
        if (!payment.getState().equals(Payment.State.WAIT_PAY)) {
            return new ReturnObject(ReturnNo.STATENOTALLOW);
        }

        if (weChatPayTransactionRetVo.getTradeState().equals("SUCCESS")) {
            payment.setState(Payment.State.HAS_PAYED);
            payment.setActualAmount(Long.valueOf(weChatPayTransactionRetVo.getAmount().getPayerTotal()));
            payment.setPayTime(weChatPayTransactionRetVo.getSuccessTime());
            // 成功通知订单
            try {
                Thread.sleep(1000);
                rocketMQService.sendSuccessOrderPayMessage(payment.getDocumentId());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        if (weChatPayTransactionRetVo.getTradeState().equals("NOTPAY")) {
            payment.setState(Payment.State.CANCELED);
        }
        payment.setTradeSn(weChatPayTransactionRetVo.getTransactionId());
        payment.setGmtModified(LocalDateTime.now());

        payDao.modifyPaymentInfo(payment, payment.getCreatorId(), payment.getCreatorName());
        return new ReturnObject();
    }

    /**
     * 微信退款回调通知
     * 对收到的退款结果，修改相应退款单信息
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public ReturnObject wechatRefundNotify(WeChatPayRefundNotifyRetVo weChatPayRefundNotifyRetVo) {

        Refund refund = (Refund) payDao.getRefundDetailInfo(Long.valueOf(weChatPayRefundNotifyRetVo.getResource().getCiphertext().getOutRefundNo())).getData();
        if (refund == null) {
            return new ReturnObject(ReturnNo.RESOURCE_ID_NOTEXIST);
        }
        if (!refund.getState().equals(Refund.State.WAIT_REFUND)) {
            return new ReturnObject(ReturnNo.STATENOTALLOW);
        }

        if (weChatPayRefundNotifyRetVo.getResource().getCiphertext().getRefundStatus().equals("SUCCESS")) {
            refund.setState(Refund.State.HAS_REFUND);
            refund.setAmount(Long.valueOf(weChatPayRefundNotifyRetVo.getResource().getCiphertext().getAmount().getRefund()));
//            // 退款成功通知订单
//            rocketMQService.sendSuccessOrderRefundMessage(refund.getDocumentId());
        }
        if (weChatPayRefundNotifyRetVo.getResource().getCiphertext().getRefundStatus().equals("ABNORMAL")) {
            refund.setState(Refund.State.CANCELED);
        }
        refund.setTradeSn(weChatPayRefundNotifyRetVo.getResource().getCiphertext().getRefundId());
        refund.setRefundTime(weChatPayRefundNotifyRetVo.getResource().getCiphertext().getSuccessTime());
        refund.setGmtModified(LocalDateTime.now());

        payDao.modifyRefundInfo(refund, refund.getCreatorId(), refund.getCreatorName());
        return new ReturnObject();
    }

    /**
     * 阿里回调通知
     * 对收到的支付或退款结果，修改相应支付或退款单信息
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public ReturnObject alipayNotify(NotifyBody notifyBody) {

        if (notifyBody.getOut_biz_no() == null) {
            Payment payment = (Payment) payDao.getPaymentById(Long.valueOf(notifyBody.getOut_trade_no())).getData();
            if (payment == null) {
                return new ReturnObject(ReturnNo.RESOURCE_ID_NOTEXIST);
            }
            if (!payment.getState().equals(Payment.State.WAIT_PAY)) {
                return new ReturnObject(ReturnNo.STATENOTALLOW);
            }

            if (notifyBody.getTrade_status().equals("TRADE_SUCCESS")) {
                payment.setState(Payment.State.HAS_PAYED);
                payment.setActualAmount(Long.valueOf(notifyBody.getBuyer_pay_amount()));
                payment.setPayTime(notifyBody.getGmt_payment());
            }
            if (notifyBody.getTrade_status().equals("WAIT_BUYER_PAY")) {
                payment.setState(Payment.State.CANCELED);
            }
            payment.setGmtModified(LocalDateTime.now());

            payDao.modifyPaymentInfo(payment, payment.getCreatorId(), payment.getCreatorName());
            return new ReturnObject();
        } else {
            Refund refund = (Refund) payDao.getRefundDetailInfo(Long.valueOf(notifyBody.getOut_biz_no())).getData();
            if (refund == null) {
                return new ReturnObject(ReturnNo.RESOURCE_ID_NOTEXIST);
            }
            if (!refund.getState().equals(Refund.State.WAIT_REFUND)) {
                return new ReturnObject(ReturnNo.STATENOTALLOW);
            }

            refund.setState(Refund.State.HAS_REFUND);
            refund.setRefundTime(notifyBody.getGmt_refund());
            refund.setGmtModified(LocalDateTime.now());

            payDao.modifyRefundInfo(refund, refund.getCreatorId(), refund.getCreatorName());
            return new ReturnObject();
        }
    }

    /**
     * @author YuJie
     * @date 2021-12-12
     */
    @Transactional(rollbackFor = Exception.class)
    public ReturnObject createPayment(NewPaymentVo newPaymentVo, Long userId, String loginName) {


        Payment target = null;

        Payment payment = cloneVo(newPaymentVo, Payment.class);
        payment.setState(Payment.State.WAIT_PAY);
        setPoCreatedFields(payment, userId, loginName);
        ReturnObject<Payment> paymentReturnObject = payDao.createPayment(payment);
        target = paymentReturnObject.getData();


        ReturnObject<PayPattern> ret = payDao.getPayPatternById(newPaymentVo.getPatternId());
        if (ret.getData() == null) return ret;
        PayPattern payPattern = ret.getData();
        String className = payPattern.getClassName();

        try {
            BasePaymentPattern pattern = payMentPatternLoader.getPaymentPatternByClassName(className);

            DoPayInfo doPayInfo = pattern.pay(target.getId().toString(), new Amount(null, newPaymentVo.getAmount(), "CNY"), newPaymentVo.getDescr());

            // 发延迟消息检查第三方支付状态
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            Constants.servletRequestAttributes = servletRequestAttributes;
            String json = JacksonUtil.toJson(target);

            rocketMQService.sendPayCheckMessage(json, Constants.FIVE_SECONDS_LEVEL);

            NewPaymentRetVo newPaymentRetVo = cloneVo(target, NewPaymentRetVo.class);
            if (!(doPayInfo == null || doPayInfo.getPrepayId() == null))
                newPaymentRetVo.setPrepayId(doPayInfo.getPrepayId());
            return new ReturnObject<>(newPaymentRetVo);
        } catch (Exception e) {
            logger.error(e.getMessage());
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR, e.getMessage());
        }
    }


    @Transactional(rollbackFor = Exception.class)
    public ReturnObject payExist(Long pid, Long patternId, Long userId, String userName) {
        ReturnObject<Payment> ret = payDao.getPaymentById(pid);
        if (ret.getData() == null) return ret;
        Payment payment = ret.getData();
        if (payment.getState() != Payment.State.WAIT_PAY) return new ReturnObject<>(ReturnNo.STATENOTALLOW, "支付单已支付");
        if (payment.getEndTime().isBefore(LocalDateTime.now()))
            return new ReturnObject(ReturnNo.STATENOTALLOW, "支付单时间已过");
        String documentId = payment.getDocumentId();
        ReturnObject<PayPattern> ret1 = payDao.getPayPatternById(patternId);
        if (ret1.getData() == null) return ret1;

        payment.setPatternId(patternId);
        ReturnObject<PaymentPo> paymentPoReturnObject = payDao.modifyPaymentInfo(payment, userId, userName);
        String className = ret1.getData().getClassName();
        try {
            BasePaymentPattern pattern = payMentPatternLoader.getPaymentPatternByClassName(className);
            pattern.pay(documentId, new Amount(null, payment.getAmount(), "CNY"), payment.getDescr());
            String json = JacksonUtil.toJson(payment);
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            Constants.servletRequestAttributes = servletRequestAttributes;

            rocketMQService.sendPayCheckMessage(json, Constants.FIVE_SECONDS_LEVEL);
            return new ReturnObject<>(cloneVo(paymentPoReturnObject.getData(), NewPaymentRetVo.class));

        } catch (Exception e) {
            logger.error(e.getMessage());
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR, e.getMessage());
        }
    }

    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public ReturnObject getPaymentByDocumentId(String documentId) {
        ReturnObject<List> paymentReturnObject = payDao.getPaymentByDocumentId(documentId);
        if (paymentReturnObject.getData() == null) return paymentReturnObject;
        ReturnObject ret = Common.getListRetVo(paymentReturnObject, InternalPaymentRetVo.class);
        return ret;
    }

    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public ReturnObject getRefundByDocumentId(String documentId) {
        ReturnObject<List<RefundPo>> refundReturnObject = payDao.getRefundByDocumentId(documentId);
        if (refundReturnObject.getData() == null) return refundReturnObject;
        List<SimpleRefundRetVo> simpleRefundRetVoList = new ArrayList<>();
        for (RefundPo refundPo : refundReturnObject.getData()) {
            SimpleRefundRetVo simpleRefundRetVo = cloneVo(refundPo, SimpleRefundRetVo.class);
            simpleRefundRetVoList.add(simpleRefundRetVo);
        }
        return new ReturnObject<>(simpleRefundRetVoList);
    }

//    @Transactional(rollbackFor = Exception.class)
    public ReturnObject createRefund(String documentId, Long amount, Byte documentType, String descr) {
        PaymentPoExample paymentPoExample = new PaymentPoExample();
        PaymentPoExample.Criteria cr = paymentPoExample.createCriteria();
        cr.andDocumentIdEqualTo(documentId);
        cr.andStateNotEqualTo(Payment.State.WAIT_PAY.getCode().byteValue());
        cr.andStateNotEqualTo(Payment.State.CANCELED.getCode().byteValue());
        cr.andStateNotEqualTo(Payment.State.FAILED_PAY.getCode().byteValue());

        ReturnObject<List> returnObject = payDao.getPaymentByExample(paymentPoExample);
        if (returnObject.getData() == null || returnObject.getData().size() != 1)
            return new ReturnObject(ReturnNo.RESOURCE_ID_NOTEXIST, "没有支付单");
        PaymentPo paymentPo = (PaymentPo) returnObject.getData().get(0);

        Long realAmount = paymentPo.getAmount();

        Long hasRefund = 0L;
        ReturnObject<List<RefundPo>> refundReturnObject = payDao.getRefundByDocumentId(documentId);
        if (!(refundReturnObject.getData() == null || refundReturnObject.getData().size() == 0)) {
            for (RefundPo refundPo : refundReturnObject.getData()) {
                hasRefund += refundPo.getAmount();
            }
        }

        if (amount + hasRefund > realAmount) return new ReturnObject(ReturnNo.REFUND_MORE);
        BasePaymentPattern paymentPattern = getBasePayPatternById(paymentPo.getPatternId());

        Refund refund = new Refund();
        refund.setPatternId(paymentPo.getPatternId());
        refund.setAmount(amount);
        refund.setPaymentId(paymentPo.getId());
        refund.setDocumentId(documentId);
        refund.setDocumentType(Refund.DocumentType.getDocumentTypeByCode(Integer.valueOf(documentType)));
        refund.setState(Refund.State.WAIT_REFUND);
        refund.setDescr(descr);
        setPoCreatedFields(refund, paymentPo.getCreatorId(), paymentPo.getCreatorName());

        ReturnObject<Refund> refundReturnObject1 = payDao.createRefund(refund);

        paymentPattern.refund(new Amount(amount, amount, "CNY"), paymentPo.getId().toString(), refundReturnObject1.getData().getId().toString(), descr);

        // 30s 延迟发消息检查第三方退款状态
        String json = JacksonUtil.toJson(refund);

        rocketMQService.sendRefundCheckMessage(json, Constants.FIVE_SECONDS_LEVEL);

        return new ReturnObject<>();
    }

    @Transactional(rollbackFor = Exception.class)
    public ReturnObject createRefundAll(String documentId, Byte documentType, String descr) {
        PaymentPoExample paymentPoExample = new PaymentPoExample();
        PaymentPoExample.Criteria cr = paymentPoExample.createCriteria();
        cr.andDocumentIdEqualTo(documentId);
        cr.andStateNotEqualTo(Payment.State.WAIT_PAY.getCode().byteValue());
        cr.andStateNotEqualTo(Payment.State.CANCELED.getCode().byteValue());
        cr.andStateNotEqualTo(Payment.State.FAILED_PAY.getCode().byteValue());

        ReturnObject<List> returnObject = payDao.getPaymentByExample(paymentPoExample);
        if (returnObject.getData() == null || returnObject.getData().size() != 1)
            return new ReturnObject(ReturnNo.RESOURCE_ID_NOTEXIST, "没有支付单");
        PaymentPo paymentPo = (PaymentPo) returnObject.getData().get(0);


        Long hasRefund = 0L;
        ReturnObject<List<RefundPo>> refundReturnObject = payDao.getRefundByDocumentId(documentId);
        if (!(refundReturnObject.getData() == null || refundReturnObject.getData().size() == 0)) {
            for (RefundPo refundPo : refundReturnObject.getData()) {
                hasRefund += refundPo.getAmount();
            }
        }

        if (hasRefund > 0) return new ReturnObject(ReturnNo.REFUND_MORE);
        BasePaymentPattern paymentPattern = getBasePayPatternById(paymentPo.getPatternId());

        Refund refund = new Refund();
        refund.setPatternId(paymentPo.getPatternId());
        refund.setAmount(paymentPo.getActualAmount());
        refund.setDocumentId(documentId);
        refund.setDocumentType(Refund.DocumentType.getDocumentTypeByCode(Integer.valueOf(documentType)));
        refund.setState(Refund.State.WAIT_REFUND);
        refund.setDescr(descr);
        setPoCreatedFields(refund, paymentPo.getCreatorId(), paymentPo.getCreatorName());

        ReturnObject<Refund> refundReturnObject1 = payDao.createRefund(refund);
        paymentPattern.refund(new Amount(refund.getAmount(), refund.getAmount(), "CNY"), documentId, refundReturnObject1.getData().getId().toString(), descr);

        // 30s 延迟发消息检查第三方退款状态
        String json = JacksonUtil.toJson(refund);

        rocketMQService.sendRefundCheckMessage(json, Constants.FIVE_SECONDS_LEVEL);

        return new ReturnObject<>();

    }


    private BasePaymentPattern getBasePayPatternById(Long id) {
        try {
            ReturnObject<PayPattern> ret = payDao.getPayPatternById(id);
            if (ret.getData() == null) return null;
            BasePaymentPattern paymentPattern = payMentPatternLoader.getPaymentPatternByClassName(ret.getData().getClassName());
            return paymentPattern;
        } catch (Exception e) {
            logger.error(e.getMessage());
            return null;
        }
    }

    public ReturnObject setLiquidatedPayment(Long id) {
        ReturnObject ret = payDao.getPaymentById(id);
        if (ret.getCode() != ReturnNo.OK) return ret;
        Payment payment = (Payment) ret.getData();
        payment.setState(Payment.State.HAS_LIQUIDATED);
        return payDao.modifyPaymentInfo(payment, payment.getCreatorId(), payment.getCreatorName());
    }

    public ReturnObject setLiquidatedRefund(Long id) {
        ReturnObject ret = payDao.getRefundById(id);
        if (ret.getCode() != ReturnNo.OK) return ret;
        Refund refund = (Refund) ret.getData();
        refund.setState(Refund.State.HAS_LIQUIDATED);
        return payDao.modifyRefundInfo(refund, refund.getCreatorId(), refund.getCreatorName());
    }

}
